module AXI4_LITE_ADAPTER (
    input               ACLK,
    input               ARESETN,
    
    // AXI-Lite Bus Interface
    input       [ 7:0]  S_AXI_ARADDR,
    output              S_AXI_ARREADY,
    input               S_AXI_ARVALID,

    input       [ 7:0]  S_AXI_AWADDR,
    output              S_AXI_AWREADY,
    input               S_AXI_AWVALID,

    output      [ 1:0]  S_AXI_BRESP,
    input               S_AXI_BREADY,
    output              S_AXI_BVALID,

    output      [31:0]  S_AXI_RDATA,
    output      [ 1:0]  S_AXI_RRESP,
    input               S_AXI_RREADY,
    output              S_AXI_RVALID,

    input       [31:0]  S_AXI_WDATA,
    output              S_AXI_WREADY,
    input               S_AXI_WVALID,
    input       [ 3:0]  S_AXI_WSTRB,

    // Registers
    output      [31:0]  SLV_REG0,
    output      [31:0]  SLV_REG1,
    output      [31:0]  SLV_REG2,
    output      [31:0]  SLV_REG3,
    output      [31:0]  SLV_REG4,
    output      [31:0]  SLV_REG5,
    output      [31:0]  SLV_REG6,
    output      [31:0]  SLV_REG7
);

reg  [31:0]  slv_reg0;      assign SLV_REG0         = slv_reg0;
reg  [31:0]  slv_reg1;      assign SLV_REG1         = slv_reg1;
reg  [31:0]  slv_reg2;      assign SLV_REG2         = slv_reg2;
reg  [31:0]  slv_reg3;      assign SLV_REG3         = slv_reg3;
reg  [31:0]  slv_reg4;      assign SLV_REG4         = slv_reg4;
reg  [31:0]  slv_reg5;      assign SLV_REG5         = slv_reg5;
reg  [31:0]  slv_reg6;      assign SLV_REG6         = slv_reg6;
reg  [31:0]  slv_reg7;      assign SLV_REG7         = slv_reg7;
reg          s_axi_arready; assign S_AXI_ARREADY    = s_axi_arready;
reg          s_axi_awready; assign S_AXI_AWREADY    = s_axi_awready;
reg  [ 1:0]  s_axi_bresp;   assign S_AXI_BRESP      = s_axi_bresp; 
reg          s_axi_bvalid;  assign S_AXI_BVALID     = s_axi_bvalid;
reg  [31:0]  s_axi_rdata;   assign S_AXI_RDATA      = s_axi_rdata;
reg  [ 1:0]  s_axi_rresp;   assign S_AXI_RRESP      = s_axi_rresp;
reg          s_axi_rvalid;  assign S_AXI_RVALID     = s_axi_rvalid;
reg          s_axi_wready;  assign S_AXI_WREADY     = s_axi_wready;

reg s_axi_aw_enable;
reg [7:0] s_axi_awaddr_buffer;
reg [7:0] s_axi_araddr_buffer;

// Slave register signals.
reg [31:0] slv_reg_dout;
wire slv_reg_rden = s_axi_arready && S_AXI_ARVALID && ~s_axi_rvalid;
wire slv_reg_wren = s_axi_wready && s_axi_awready && S_AXI_AWVALID;

// Implement AWREADY generation.
always @(posedge ACLK) begin
    if (!ARESETN) begin
        s_axi_awready <= 1'b0;
        s_axi_aw_enable <= 1'b1;
    end
    else begin
        if (S_AXI_AWVALID && ~s_axi_awready && s_axi_aw_enable && S_AXI_WVALID) begin
            s_axi_awready <= 1'b1; 
            s_axi_aw_enable <= 1'b0;
        end
        else if (s_axi_bvalid && S_AXI_BREADY) begin
            s_axi_aw_enable <= 1'b1;
            s_axi_awready <= 1'b0;
        end
        else begin
            s_axi_awready <= 1'b0;
        end
    end
end

// Implement AWADDR latching.
always @(posedge ACLK) begin
    if (!ARESETN) begin
        s_axi_awaddr_buffer <= 32'b0;
    end
    else begin
        if(~s_axi_awready && S_AXI_AWVALID && S_AXI_WVALID && s_axi_aw_enable) begin
            s_axi_awaddr_buffer <= S_AXI_AWADDR;
        end
    end
end

// Implement WREADY generation.
always @(posedge ACLK) begin
    if (!ARESETN) begin
        s_axi_wready <= 1'b0;
    end
    else begin
        if(~s_axi_wready && S_AXI_WVALID && S_AXI_AWVALID && s_axi_aw_enable) begin
            s_axi_wready <= 1'b1;
        end
        else begin
            s_axi_wready <= 1'b0;
        end
    end
end

// Implement ARREADY generation and ARADDR latching.
always @(posedge ACLK) begin
    if(!ARESETN) begin
        s_axi_arready <= 1'b0;
        s_axi_araddr_buffer <= 32'b0;
    end
    else begin
        if (~s_axi_arready && S_AXI_ARVALID) begin
            s_axi_arready <= 1'b1;
            s_axi_araddr_buffer <= S_AXI_ARADDR;
        end
        else begin
            s_axi_arready <= 1'b0;
        end
    end
end

// Implement RVALID generation.
always @(posedge ACLK) begin
    if (!ARESETN) begin
        s_axi_rvalid <= 1'b0;
        s_axi_rresp <= 2'b0;
    end
    else begin
        if (s_axi_arready && S_AXI_ARVALID && ~s_axi_rvalid) begin
            s_axi_rvalid <= 1'b1;
            s_axi_rresp <= 2'b0;
        end
        else if(s_axi_rvalid && S_AXI_RREADY) begin
            s_axi_rvalid <= 1'b0;
        end
    end
end

// Implement RDATA generation.
always @(posedge ACLK) begin
    if (!ARESETN) begin
        s_axi_rdata <= 32'b0;
    end
    else begin
        if (slv_reg_rden) begin
            s_axi_rdata <= slv_reg_dout;
        end
    end
end

// Implement BVALID generation.
always @(posedge ACLK) begin
    if (!ARESETN) begin
        s_axi_bvalid <= 1'b0;
        s_axi_bresp <= 1'b0;
    end
    else begin
        if(s_axi_awready && S_AXI_AWVALID && S_AXI_WVALID && s_axi_wready && ~s_axi_bvalid) begin
            s_axi_bvalid <= 1'b1;
            s_axi_bresp <= 2'b0;
        end
        else begin
            if (s_axi_bvalid && S_AXI_BREADY) begin
                s_axi_bvalid <= 1'b0;
            end
        end
    end
end

// Get write strobe signal and write data.
function [31:0] write_strobe_func;
    input [31:0]    src_data;
    input [31:0]    write_data;
    input [3:0]     strobe;
    begin
        if (strobe[0]) write_strobe_func[7:0] = write_data[7:0];         else write_strobe_func[7:0] = src_data[7:0];
        if (strobe[1]) write_strobe_func[15:8] = write_data[15:8];       else write_strobe_func[15:8] = src_data[15:8];
        if (strobe[2]) write_strobe_func[23:16] = write_data[23:16];     else write_strobe_func[23:16] = src_data[23:16];
        if (strobe[3]) write_strobe_func[31:24] = write_data[31:24];     else write_strobe_func[31:24] = src_data[31:24];
    end
endfunction



// Implement memory mapped register read process.

always @(*) begin
    case (s_axi_araddr_buffer[7:2])
        6'h0:   slv_reg_dout <= slv_reg0;
        6'h1:   slv_reg_dout <= slv_reg1;
        6'h2:   slv_reg_dout <= slv_reg2;
        6'h3:   slv_reg_dout <= slv_reg3;
        6'h4:   slv_reg_dout <= slv_reg4;
        6'h5:   slv_reg_dout <= slv_reg5;
        6'h6:   slv_reg_dout <= slv_reg6;
        6'h7:   slv_reg_dout <= slv_reg7;
        default:slv_reg_dout <= 32'b0;
    endcase
end

always @(posedge ACLK) begin
    if (!ARESETN) begin
        slv_reg0 <= 32'b0;
        slv_reg1 <= 32'b0;
        slv_reg2 <= 32'b0;
        slv_reg3 <= 32'b0;
        slv_reg4 <= 32'b0;
        slv_reg5 <= 32'b0;
        slv_reg6 <= 32'b0;
        slv_reg7 <= 32'b0;
    end
    else begin
        if (slv_reg_wren) begin
            case(s_axi_awaddr_buffer[7:2])
                6'h0:   slv_reg0 <= write_strobe_func(slv_reg0,S_AXI_WDATA,S_AXI_WSTRB);
                6'h1:   slv_reg1 <= write_strobe_func(slv_reg1,S_AXI_WDATA,S_AXI_WSTRB);
                6'h2:   slv_reg2 <= write_strobe_func(slv_reg2,S_AXI_WDATA,S_AXI_WSTRB);
                6'h3:   slv_reg3 <= write_strobe_func(slv_reg3,S_AXI_WDATA,S_AXI_WSTRB);
                6'h4:   slv_reg4 <= write_strobe_func(slv_reg4,S_AXI_WDATA,S_AXI_WSTRB);
                6'h5:   slv_reg5 <= write_strobe_func(slv_reg5,S_AXI_WDATA,S_AXI_WSTRB);
                6'h6:   slv_reg6 <= write_strobe_func(slv_reg6,S_AXI_WDATA,S_AXI_WSTRB);
                6'h7:   slv_reg7 <= write_strobe_func(slv_reg7,S_AXI_WDATA,S_AXI_WSTRB);
                default: begin
                    slv_reg0 <= slv_reg0;
                    slv_reg1 <= slv_reg1;
                    slv_reg2 <= slv_reg2;
                    slv_reg3 <= slv_reg3;
                    slv_reg4 <= slv_reg4;
                    slv_reg5 <= slv_reg5;
                    slv_reg6 <= slv_reg6;
                    slv_reg7 <= slv_reg7;
                end
            endcase
        end
    end
end

endmodule
